package net.jcip.examples;

import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;

public class Memoizer<A,V> implements Computable <A,V>{

	private static final int EXPIRATION_TIME = 5000;
	private final Computable<A,V> computer;
	private final ConcurrentMap<A, FutureTask<V>> cache = new ConcurrentHashMap<>();
	private final ConcurrentMap<A, Long> livingTimeMap = new ConcurrentHashMap<>();
	
	public Memoizer(Computable<A, V> computer) {
        super();
        this.computer = computer;
	}
	
	@Override
	public V compute(final A arg) throws InterruptedException {
		Long livingTime = livingTimeMap.get(arg);
		if ( (livingTime!=null) && (getCurrentTime() - EXPIRATION_TIME ) > livingTime) {
			livingTimeMap.remove(arg);
		}
		
		Future<V> future = cache.get(arg);
		
		if (future == null){
			FutureTask<V> futureTask = new FutureTask<>(new Callable<V>() {
	            @Override
	            public V call() throws InterruptedException {
	                V result = computer.compute(arg);
	                livingTimeMap.put(arg, getCurrentTime());
	                return result;
	            }
			});
			
			future = cache.put(arg, futureTask);
			futureTask.run();
		}
		try {
			return future.get();
		} catch (ExecutionException e) {
			e.printStackTrace();
			cache.remove(arg);
		}
		return null;
	}
	
	private long getCurrentTime(){
		return System.currentTimeMillis();
	}

}
